package com.seed.fileud.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.seed.fileud.pojo.FileStatus;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class LocalFileudService implements FileudService {
    private final String root;
    private String part = ".part";

    public LocalFileudService(Map<String, Object> config) {
        if(config == null) throw new RuntimeException("请添加本地文件上传配置");

        String path = (String) config.get("path");
        if(StrUtil.isBlank(path)) throw new RuntimeException("本地文件上传【path】必需配置");

        this.root = path;

        String part = (String) config.get("part");
        if(StrUtil.isNotBlank(part)) this.part = part;
    }

    @Override
    public FileStatus validate(String bucket, String path, String hash, String fileName, Integer chunkCount) {
        path = formatPath(path);
        String ext = getSuffix(fileName);
        String mainName = FileUtil.mainName(fileName);
        String hashFileName = mainName + "." + hash + "." + ext;
        String filePath = bucket + "/" + path + hashFileName;
        if(fileExist(filePath)) return FileStatus.yes(filePath);

        List<Integer> chunkList = getChunkList(bucket, path, hash);
        Integer size = chunkList.size();
        if(size.equals(chunkCount)) return FileStatus.all();
        if(size > 0) return FileStatus.part(chunkList);

        List<File> fileList = FileUtil.loopFiles(root, f -> {
            String e = FileUtil.getSuffix(f);
            String h = getHash(f.getName());
            return hash.equals(h) && ext.equals(e);
        });
        if(fileList.isEmpty()) return FileStatus.no();

        File srcFile = fileList.get(0);
        File descFile = new File(root + "/" + filePath);
        FileUtil.copy(srcFile, descFile, true);
//        log.info("文件拷贝：" + descFile.getAbsolutePath());

        return FileStatus.yes(filePath);
    }

    @Override
    public String uploadFile(String bucket, String path, String hash, MultipartFile multipartFile) {
        path = formatPath(path);
        String fileName = multipartFile.getOriginalFilename();
        String ext = getSuffix(fileName);
        String mainName = FileUtil.mainName(fileName);
        String hashFileName = mainName + "." + hash + "." + ext;
        String filePath = bucket + "/" + path + hashFileName;
        String fileFullPath = root + "/" + filePath;
        try {
            multipartFile.transferTo(new File(fileFullPath));
            return filePath;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void uploadChunk(String bucket, String path, String hash, Integer chunkIndex, MultipartFile multipartFile) {
        path = formatPath(path);

        String fileFullPath = root + "/" + bucket + "/" + path + hash + "/" + chunkIndex + part;
        File chunk = new File(fileFullPath);
        File parent = chunk.getParentFile();
        if(!parent.exists()) parent.mkdirs();

        try {
            multipartFile.transferTo(chunk);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String merge(String bucket, String path, String hash, String fileName) {
        path = formatPath(path);
//        String filePath = root + "/" + bucket + "/" + path + hash;
        File parent = new File(root + "/" + bucket + "/" + path + hash);
        File[] files = parent.listFiles();
        if(files == null) throw new RuntimeException("文件块不存在");

        List<File> fileList = Arrays.stream(files).sorted((f1, f2) -> {
            Integer i1 = getChunkIndex(f1.getName());
            Integer i2 = getChunkIndex(f2.getName());
            return i1 - i2;
        }).toList();

        String ext = getSuffix(fileName);
        String mainName = FileUtil.mainName(fileName);
        String hashFileName = mainName + "." + hash + "." + ext;
        String filePath = bucket + "/" + path + hashFileName;
        String fileFullPath = root + "/" + filePath;
        try {
            RandomAccessFile newFile = new RandomAccessFile(new File(fileFullPath), "rw");
            long position = 0;
            for(File file: fileList) {
                // 读取文件中的内容
                RandomAccessFile accessFile = new RandomAccessFile(file, "r");
                FileChannel fileChannel = accessFile.getChannel();
                long fileSize = fileChannel.size();
                byte[] bs = new byte[(int) fileSize];
                accessFile.read(bs);
                newFile.seek(position);
                newFile.write(bs);
                position += fileSize;
                fileChannel.close();
                file.delete();
            }
            newFile.close();
            parent.delete();
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("合并异常");
        }
    }

    @Override
    public void remove(String filePath) {
        if(filePath.startsWith("/")) filePath = filePath.substring(1);
        String fileFullPath = root + "/" + filePath;
        File file = new File(fileFullPath);
        if(!file.exists()) throw new RuntimeException("文件不存在");

        if(!file.delete()) throw new RuntimeException("文件删除失败");
    }

    @Override
    public File file(String filePath) {
        if(filePath.startsWith("/")) filePath = filePath.substring(1);
        String fileFullPath = root + "/" + filePath;
        File file = new File(fileFullPath);
        if(!file.exists()) throw new RuntimeException("文件不存在");

        return file;
    }

    @Override
    public byte[] readFile(String filePath, Long offset, Long length) {
        if(filePath.startsWith("/")) filePath = filePath.substring(1);
        String fileFullPath = root + "/" + filePath;
        File file = new File(fileFullPath);
        if(!file.exists()) throw new RuntimeException("文件不存在");

        try {
            RandomAccessFile accessFile = new RandomAccessFile(file, "r");
            FileChannel fileChannel = accessFile.getChannel();
            long fileSize = fileChannel.size();
            if(fileSize <= offset) throw new RuntimeException("文件下载出错，偏移量大于文件");

            byte[] bs = new byte[Math.toIntExact(length)];
            accessFile.seek(offset);
            accessFile.read(bs);

            fileChannel.close();
            accessFile.close();
            return bs;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String formatPath(String path) {
        if(!path.endsWith("/")) path = path + "/";
        if(path.startsWith("/")) path = path.substring(1);

        return path;
    }

    private String getSuffix(String fileName) {
        String ext = FileUtil.getSuffix(fileName);
        if(StrUtil.isBlank(ext)) throw new RuntimeException("必须有扩展名");

        return ext;
    }

    private String getHash(String fileName) {
        String s = fileName.substring(0, fileName.lastIndexOf('.'));
        return s.substring(s.lastIndexOf('.') + 1);
    }

    /**
     * 检查文件是否存在
     * @param filePath: bucket1/2024/07/27/毛泽东第二集.abcxye1234594343wx.mp4
     * @return boolean
     */
    private boolean fileExist(String filePath) {
        String fileFullPath = root + "/" + filePath;
        File file = new File(fileFullPath);
        return file.exists() && !file.isDirectory();
    }

    private List<Integer> getChunkList(String bucket, String path, String hash) {
        String parentPath = root + "/" + bucket + "/" + path + hash;
        File parent = new File(parentPath);
        if(!parent.exists()) return Collections.emptyList();

        String[] items = parent.list();
        if(items == null) return Collections.emptyList();

        return Arrays.stream(items)
                .filter(e -> e.endsWith(part))
                .map(this::getChunkIndex)
                .toList();
    }

    private Integer getChunkIndex(String fileName) {
        return Integer.valueOf(fileName.substring(0, fileName.length() - part.length()));
    }

//    private static FileDTO fileNameToDTO(String filePath) {
//        if(filePath.startsWith("/")) filePath = filePath.substring(1);
//
//        int a = filePath.indexOf('/');
//        int b = filePath.lastIndexOf('/');
//        String bucket = filePath.substring(0, a);
//        String fileName = filePath.substring(b + 1);
//        String path = filePath.substring(a + 1, b);
//        System.out.println("bucket===" + bucket);
//        System.out.println("fileName===" + fileName);
//        System.out.println("path===" + path);
//
//        FileDTO fileDTO = new FileDTO();
//        fileDTO.setBucket(bucket);
//        fileDTO.setPath(path);
//        fileDTO.setFileName(fileName);
//
//        return fileDTO;
//    }

    public static void main(String[] args) throws Exception {
//        getChunkList("bucket1", "2024/07/27/", "a12345");
//        LocalFileService service = new LocalFileService();
//        service.verify("bucket1", "2024/07/27/", "b12345", "aa.jpg", 10);
//        String filePath = "bucket1/2024/07/27/af69316524795efcfece6499a5a6b1ed.mp4";
//        fileNameToDTO(filePath);
        System.out.println(FileUtil.mainName("E:\\xx/abc123"));
    }
}
